TRY (try-block, unwind-block)
CATCH (domain, catch-form)
UNWIND_PROTECT (protected-block, cleanup-block)
continue
break
tryreturn (lvalue)
THROW (exception)
THROW_TYPED (exception, type)
void exc_throw (void *exception_addr)
void exc_throw_typed (void *exception_addr, void *exception_type)
void exc_rethrow (void)
void *exception
void *exc_exception (void)
void *exc_type (void)
int EXC_IN_DOMAIN (exception, domain)
int EXC_EQUAL (exception1, exception2)
int exc_in_domain (void *exception_addr, void *domain_addr, unsigned long domain_size)
int exc_equal (void *exception_addr1, void *exception_addr2)
typedef int (*excHandler) (void *exception_addr, void *exception_type, void *handler_data)
void EXC_INSTALL_HANDLER (domain, excHandler handler, void *handler_data)
void EXC_REMOVE_HANDLER (domain, excHandler handler, void *handler_data)
void exc_install_handler (void *domain_addr, unsigned long domain_size, excHandler handler, void *handler_data)
void exc_remove_handler (void *domain_addr, unsigned long domain_size, excHandler handler, void *handler_data)
int exc_any
EXC_ANY
typedef enum
{
} excCallbackTag
typedef void (*excCallback) (excCallbackTag tag, void *callback_data, void **try_data)
void exc_install_callback (excCallbackTag tags, excCallback callback, void *callback_data)
void exc_remove_callback (excCallbackTag tags, excCallback callback, void *callback_data)
int exc_sigmask
TRY() associates two blocks of code, the try-block and the unwind-block. If an exception is thrown with THROW() in the try-block, the control of execution will be transferred immediately to the corresponding unwind-block. The argument of THROW() is a variable whose address identifies the exception. Any lvalue can be used, but care should be taken with automatic variables, since they may go out of scope when the exception is thrown.
The unwind-block may contain one or several CATCH() macros. Each CATCH() associates a code segment, catch-form, with a specific exception or an exception domain. When an exception has been thrown, the catch-form of the first CATCH() with a matching tag (domain) will be executed. Then, execution will continue after the TRY() macro. If there are no CATCH() macros, or if none of them match the exception thrown, control will be transferred to the unwind-block of the closest surrounding TRY() (if any).
A continue statement at the outer-most level in a try-block transfers control to the point just beyond the TRY() macro. break and return are not allowed in try-blocks and will generate run-time error messages if they are used there. However, the special tryreturn() macro can return an lvalue, but not a general expression.
continue is also allowed in unwind-blocks but should only be used there when the program has recovered from the exception so that normal execution may resume. If a continue is encountered, the rest of the unwind-block will be skipped. The CATCH() macro inserts an invisible continue at the end of catch-form, but continue may still be used explicitly in a catch-form to leave the CATCH (and the TRY) prematurely.
A break in a catch-form forces execution to continue after the CATCH() in the same unwind-block. break is not allowed in an unwind-block outside CATCH(). return is not allowed in unwind-blocks at all; use tryreturn() instead. tryreturn() cannot be used outside try-blocks and unwind-blocks.
Of course, break and continue have their usual meaning inside switch, while and do statements used in try-blocks and unwind-blocks.
THROW() is allowed in unwind-blocks, both inside and outside CATCH() macros.
exception is a local variable that is defined in unwind-blocks, and only there. It contains the current (pending) exception, i.e. the address of the exception variable that has been thrown. The current exception can be thrown again in an unwind-block with THROW(exception). (That is a special form: it is the pending exception and not the address of the local variable that is thrown. Changing the value of exception by direct assignment has no effect.)
Occasionally, it is convenient to handle exception variable addresses, instead of specifying the exception variables themselves. exc_throw() throws an exception identified by exception_addr. Thus, THROW(e) is equivalent to exc_throw(&e). Since the exception variable is not available in functions called from unwind-blocks, there is a corresponding function exc_exception() which returns the current exception. The value can be used in a subsequent exc_throw() call, such as exc_throw(exc_exception()). There is a special function for that particular combination, namely exc_rethrow(). Note that it is an error to call exc_exception() if there is no exception pending.
UNWIND_PROTECT() is similar to TRY(), but cleanup-block will always be executed, whether an exception is thrown in protected-block or not. Thus, it is similar to the unwind-protect special form in Common Lisp. However, if protected-block is exited with tryreturn() or goto(), cleanup-block will not be executed.
There is a corresponding function, exc_install_handler(), which accepts the address of an exception domain variable. The domain_size parameter value must be sizeof(domain). As a special case, the predefined symbol exc_any can be specified as the domain argument of EXC_INSTALL_HANDLER(), in which case the handler will be called for all exceptions thrown beyond the top-level TRY(). (Specifying EXC_ANY or NULL to exc_install_handler() has the same effect.)
A handler can be removed with EXC_REMOVE_HANDLER() or exc_remove_handler(). The arguments must match the corresponding installation call exactly, or an error message will be printed.
When the handler is called, exception_addr will contain the pending exception, and exception_type will contain the corresponding type tag (see below). These two parameters can be used for catching certain exceptions or exception types, while ignoring others.
The handler_data argument is private to the handler and is not interpreted by EXC_INSTALL_HANDLER().
If the handler returns, the system will consider the pending exception to be completely processed and will call exit() with the status value returned by the handler. On the other hand, if the handler throws a new exception or calls exc_rethrow(), the next applicable handler will be called. No handler should call exit() or abort() explicitly.
Callbacks can be removed with exc_remove_callback(). All parameters must match the ones in the corresponding installation call, or an error message will be generated.
The callback_data parameter corresponds to handler_data for exception handlers and is private to the callback function. The try_data argument, on the other hand, will contain the address of a void pointer variable, which is allocated for each installed callback every time a TRY() is entered. The pointer variable is writable and can be used by the callback for any purpose. Usually, the callback stores information associated with the TRY() in it during the excBeginCallback call. This information can be used later on, when the function is called with excEndCallback or excThrowCallback. If a callback is installed inside a try-block, the try_data pointer will be NULL when the corresponding TRY() is exited (either normally or with a throw).
Note that an exception does not necessarily indicate an error. For example, the UNWIND_PROTECT() macro does always throw an exception. However, most libraries provide a special error function that calls exc_throw(). It is sometimes more convenient to set a breakpoint in such a function than in exc_breakpoint().
TRY (
{
[1] H. Winroth, Exception Handling in ANSI C, CVAP Tech. Rep., March 1993.